package com.hitqz.robot.driver.netty.handler;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.util.concurrent.RateLimiter;
import com.hitqz.robot.api.business.event.TrackPowerEvent;
import com.hitqz.robot.api.business.event.TrackStopTriggerEvent;
import com.hitqz.robot.api.business.event.TrackUltrasonicTriggerEvent;
import com.hitqz.robot.api.common.dto.robot.RobotOnlineEventDto;
import com.hitqz.robot.driver.communication.NetworkServiceThreadPool;
import com.hitqz.robot.driver.dto.*;
import com.hitqz.robot.driver.netty.protocol.message.TrackMessage;
import com.hitqz.robot.driver.netty.router.TrackMessageRouter;
import com.hitqz.robot.driver.netty.strategy.TrackMessageHandlerStrategy;
import com.hitqz.robot.driver.util.TrackCacheUtil;
import com.hitqz.robot.driver.util.TrackToolkit;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Optional;

@Slf4j
public class TrackMessageHandler extends ChannelInboundHandlerAdapter {

    private static final RateLimiter logLimiter = RateLimiter.create(1);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理接收到的消息，这里假设 msg 是你解码后的对象
        if (msg instanceof TrackMessage message) {
            // 在这里处理消息，比如解析内容、处理业务逻辑等
            handleMessage(ctx, message);
        } else {
            log.warn("[track]channel read unknown message type: {}", msg.getClass().getName());
        }
    }

    // 处理消息的业务逻辑
    private void handleMessage(ChannelHandlerContext ctx, TrackMessage message) {
        // 根据命令进行分发
        NetworkServiceThreadPool.dealMessage(() -> {
            //异步分发给不同的 handler 用到反射,handler主线程不要做耗时间的操作,否则会阻塞线程
            byte[] data = message.getData();
            byte mainCmd = data[0];
            TrackMessageHandlerStrategy strategy = TrackMessageRouter.getHandlerStrategy(mainCmd);
            if (strategy == null) {
                this.simpleHandle(ctx, message);
            }else {
                strategy.handle(ctx, message);
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 处理异常
        log.error("[track]channel exception caught in TrackMessageHandler", cause);
        ctx.close();  // 关闭连接
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 当连接建立时的处理逻辑
        log.info("[track]channel connection active: {}", ctx.channel().remoteAddress());
        super.channelActive(ctx);
        // 查询机器人编码
        byte[] date = new byte[]{0x03};
        TrackMessage trackMessage = new TrackMessage(date);
        ctx.writeAndFlush(trackMessage);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //离线状态
        if (Objects.equals(ctx,TrackToolkit.ctx)) {
            RobotOnlineEventDto dto = new RobotOnlineEventDto();
            dto.setOnlineStatus(0);
            SpringUtil.getApplicationContext().publishEvent(dto);
            TrackToolkit.ctx = null;
        }
        // 当连接关闭时的处理逻辑
        log.info("[track]channel connection inactive: {}", ctx.channel().remoteAddress());
        super.channelInactive(ctx);
    }


    /**
     * 简单处理一些没有子命令的消息
     *
     * @param ctx
     * @param message
     */
    private void simpleHandle(ChannelHandlerContext ctx, TrackMessage message) {
        // 获取消息中的数据
        byte[] data = message.getData();
        // 分配 ByteBuf 并将数据写入
        ByteBuf buffer = ctx.alloc().buffer(data.length);
        buffer.writeBytes(data);
        try {
            // 读取命令字节
            int cmd = buffer.readByte()&0xFF;// 转换成无符号
            switch (cmd) {
                case 0x03: {
                    // 获取生产序列号，字符串字节序不影响
                    byte[] remainingData = new byte[buffer.readableBytes()];
                    buffer.readBytes(remainingData);
                    String sn = new String(remainingData, StandardCharsets.UTF_8).trim();
                    log.info("[track] get robot serial number:{}", sn);
                    if (Objects.equals(sn,TrackToolkit.serialNumber)){
                        TrackToolkit.ctx = ctx;
                        log.info("[track] get ChannelHandlerContext:{} success",ctx);
                        //上线状态
                        RobotOnlineEventDto dto = new RobotOnlineEventDto();
                        dto.setOnlineStatus(1);
                        SpringUtil.getApplicationContext().publishEvent(dto);
                    }else {
                        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
                        String clientIp = remoteAddress.getAddress().getHostAddress();
                        TrackToolkit.blacklist.add(clientIp);
                        ctx.close();
                        log.warn("[track]serial number wrong ,block ip:{}",clientIp);
                    }
                    break;
                }
                case 0x0B: {
                    //一些故障码 现在被 6420 替代
                    break;
                }
                case 0x16:{
                    break;
                }
                case 0x21:{
                    // 获取硬急停和软开关状态
                    StopStatusDto lastStopDto = Optional.ofNullable((StopStatusDto) TrackCacheUtil.get(TrackCacheUtil.KEY_STOP_STATUS)).orElse(StopStatusDto.builder().build());
                    int status = buffer.readByte()&0xff;
                    int hardStop = 0,stopBar=0,ultrasonic= 0;
                    if (status!=0){
                        hardStop = (status&0x01) == 0 ? 0 : 1;
                        stopBar = (status&0x02) == 0 ? 0 : 1;
                        ultrasonic = (status&0x04) == 0 ? 0 : 1;
                        log.info("[track]hardStop:{},stopBar:{},ultrasonic:{}",hardStop,stopBar,ultrasonic);
                    }
                    //发布track急停触发/恢复事件
                    if (lastStopDto.getStopBar()!= stopBar) {
                        SpringUtil.getApplicationContext().publishEvent(TrackStopTriggerEvent.builder()
                                .stopTriggerStatus(stopBar).build());
                    }
                    //保存返回
                    TrackCacheUtil.put(TrackCacheUtil.KEY_STOP_STATUS, StopStatusDto.builder().stopBar(stopBar).hardStop(hardStop).ultrasonic(ultrasonic).build());
                    break;
                }
                case 0x24:{
                    // 获取全部超声波值
                    String lastDataStr = Optional.ofNullable((String) TrackCacheUtil.get(TrackCacheUtil.KEY_ULTRASONIC_REALTIME_DATA)).orElse("");
                    StringBuilder sb = new StringBuilder();
                    int count = buffer.readableBytes();
                    for (int i = 0; i < (count / 2); i++) {
                        short distance = buffer.readShortLE();
                        sb.append("超声传感器[").append(i).append("]数据").append(":").append(distance).append("mm").append("\n");
                    }
                    if (!Objects.equals(lastDataStr, sb.toString())) {
                        log.info("[track]current {}", sb);
                    }
                    TrackCacheUtil.put(TrackCacheUtil.KEY_ULTRASONIC_REALTIME_DATA,sb.toString());
                    break;
                }
                case 0x27:{
                    // 获取超声波避障距离
                    int count = buffer.readableBytes();
                    short[] distances = new short[count / 2];
                    for (int i = 0; i < (count / 4); i++) {
                        //触发距离
                        short triggerDistance = buffer.readShortLE();
                        distances[i] = triggerDistance;
                        //解除距离
                        short releaseDistance = buffer.readShortLE();
                        distances[i+1] = releaseDistance;
                    }
                    log.info("[track]get ultrasonic distance config:{}",distances);
                    //保存返回
                    TrackCacheUtil.put(TrackCacheUtil.KEY_ULTRASONIC_DISTANCE,
                            UltrasonicDistanceConfigDto.builder().frontUltrasonicTriggerDistance(distances[0])
                                    .frontUltrasonicReleaseDistance(distances[1])
                                    .backUltrasonicTriggerDistance(distances[2])
                                    .backUltrasonicReleaseDistance(distances[3])
                                    .build());
                    break;
                }
                case 0x29:{
                    // 获取避障使能
                    byte ultrasonicEnable = buffer.readByte();
                    byte collisionAvoidanceEnabled = buffer.readByte();
                    byte dropAvoidanceEnabled = buffer.readByte();
                    byte infraredObstacleEnabled = buffer.readByte();
                    byte safetyRadarEnabled = buffer.readByte();
                    log.info("[track]get obstacle avoidance enable:超{},撞{},跌{},红{},安{}",ultrasonicEnable, collisionAvoidanceEnabled, dropAvoidanceEnabled, infraredObstacleEnabled, safetyRadarEnabled);
                    //保存返回
                    TrackCacheUtil.put(TrackCacheUtil.KEY_OBSTACLE_AVOIDANCE_ENABLE,
                            ObstacleAvoidanceEnableDto.builder().ultrasonicEnable(ultrasonicEnable)
                                    .collisionAvoidanceEnabled(collisionAvoidanceEnabled)
                                    .dropAvoidanceEnabled(dropAvoidanceEnabled)
                                    .infraredObstacleEnabled(infraredObstacleEnabled)
                                    .safetyRadarEnabled(safetyRadarEnabled)
                                    .build()
                    );
                    break;
                }
                case 0x40:{
                    // 获取电源相关信息 % V A
                    PowerInfoDto lastPowerInfo = Optional.ofNullable((PowerInfoDto) TrackCacheUtil.get(TrackCacheUtil.KEY_POWER_INFO)).orElse(PowerInfoDto.builder().build());
                    short percentage = buffer.readShortLE();
                    float voltage = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100),2, RoundingMode.HALF_UP).floatValue();
                    float current = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100),2, RoundingMode.HALF_UP).floatValue();
                    float capacitance = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(10),1, RoundingMode.HALF_UP).floatValue();
                    float temperature = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(10),1, RoundingMode.HALF_UP).floatValue();
                    short currentDirection = buffer.readShortLE();
                    if (lastPowerInfo.getPercentage()!= percentage || Math.abs(lastPowerInfo.getVoltage()- voltage)>1 || Math.abs(lastPowerInfo.getCurrent()- current)>1 || lastPowerInfo.getCurrentDirection()!= currentDirection) {
                        log.info("[track]robot power:{}%,{}V,{}A,cap:{},{}℃,direction:{}", percentage, voltage, current, capacitance, temperature, currentDirection);
                    }
                    PowerInfoDto powerInfoDto = PowerInfoDto.builder().percentage(percentage).voltage(voltage).current(current).capacitance(capacitance).temperature(temperature).currentDirection(currentDirection).build();
                    TrackPowerEvent trackPowerEvent = BeanUtil.copyProperties(powerInfoDto, TrackPowerEvent.class);
                    //发布电量事件
                    SpringUtil.getApplicationContext().publishEvent(trackPowerEvent);
                    //保存返回
                    TrackCacheUtil.put(TrackCacheUtil.KEY_POWER_INFO, powerInfoDto);
                    break;
                }
                case 0x41:{
                    break;
                }
                case 0x42:{
                    Byte lastStatus = Optional.ofNullable((Byte) TrackCacheUtil.get(TrackCacheUtil.KEY_CHARGE_STATUS)).orElse((byte) 0);
                    // 获取回充状态  0-正常速度控制 1-启动回充标志 2-未找到充电桩发出的红外信号 3：收到充电桩发出的握手信号 4：充电正常进行中 5：充电过程中出现异常 6：电池电量充满，充电结束 7：充电停止后退 8：充电过程中出现拍硬急停或者碰撞开关 9：充电极电压检测故障
                    byte chargeStatus = buffer.readByte();
                    if (lastStatus!=chargeStatus) {
                        log.info("[track]charge status:{}", chargeStatus);
                    }
                    //保存返回
                    TrackCacheUtil.put(TrackCacheUtil.KEY_CHARGE_STATUS, chargeStatus);
                    //todo 发布充上电事件
                    break;
                }
                case 0x54:{
                    // 获取空气质量
                    AirQualityDto lastAirQuality = Optional.ofNullable((AirQualityDto) TrackCacheUtil.get(TrackCacheUtil.KEY_AIR_QUALITY)).orElse(AirQualityDto.builder().build());
                    float co = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(10),1, RoundingMode.HALF_UP).floatValue();
                    float so2 = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100),2, RoundingMode.HALF_UP).floatValue();
                    float o2 = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100),2, RoundingMode.HALF_UP).floatValue();
                    float ch4 = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(10),1, RoundingMode.HALF_UP).floatValue();
                    short temperature = buffer.readShortLE();
                    short humidity = buffer.readShortLE();
                    short co2 = buffer.readShortLE();
                    float no2 = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100),2, RoundingMode.HALF_UP).floatValue();
                    if (logLimiter.tryAcquire(60 * 5)) {
                        log.info("[track] 54 air:co:{},so2:{},o2:{},ch4:{},temperature:{},humidity:{},co2:{},no2:{}", co, so2, o2, ch4, temperature, humidity, co2, no2);
                    }
                    //保存返回
                    AirQualityDto airQualityDto = AirQualityDto.builder().co(co).so2(so2).o2(o2).ch4(ch4).temperature(temperature).humidity(humidity).co2(co2).no2(no2).build();
                    BeanUtil.copyProperties(airQualityDto,lastAirQuality);
                    TrackCacheUtil.put(TrackCacheUtil.KEY_AIR_QUALITY,lastAirQuality);
                    break;
                }
                case 0x65:{
                    // 65 基本是设置类的命令
                    byte subCmd = buffer.readByte();
                    byte result = buffer.readByte();
                    TrackToolkit.release(message,true,result == (byte) 0x00);
                    if (result != (byte) 0){
                        log.warn("[track]65{} set {},result:{}",String.format("%02x",subCmd),result==(byte)0x01?"failed":"data error",result);
                    }
                    break;
                }
                case 0x97:{
                    // 获取灯状态 0x00 关闭 0x01 打开
                    byte lightStatus = buffer.readByte();
                    log.info("[track]light status:{}",lightStatus);
                    break;
                }
                default:
                    log.warn("[track] unknown main cmd: {},strategy no found!", String.format("%02X", cmd));
                    break;
            }
            TrackToolkit.release(message, false, true);
        } catch (Exception e) {
            log.warn("[track]simpleHandle error", e);
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }

    }


}
